import { Asset, assetManager, Camera, Canvas, game, gfx, instantiate, Node, Prefab, renderer, size, UITransform, Widget } from "cc";
import { UIBase } from "./UIBase";
import { UILayer } from "./UILayer";
import { loadAysnc } from "./UILoadHelper";
import { UIReference } from "./UIReference";

export type PanelInfo = { prefabUrl?: string, bundleUrl?: string, UILayer?: UILayer }
const uiPanels = new Map<any, PanelInfo>();
export function UI(panelInfo: { prefabUrl?: string, bundleUrl?: string, UILayer?: UILayer } = {}) {
    return function (target) {
        if (!panelInfo.bundleUrl) panelInfo.bundleUrl = "UI";
        if (!panelInfo.UILayer) panelInfo.UILayer = UILayer.Common;
        uiPanels.set(target.prototype, panelInfo);
    }
}
export class UIPanelInfo {
    public UIType: any;
    public UILayer: UILayer;
}
export class UIManager extends et.Entity {
    private static instance: UIManager;
    public static get Instance(): UIManager {
        return this.instance;
    }
    public static set Instance(value: UIManager) {
        this.instance = value;
    }
    public UIRoot: Node;
    private uiDic: Map<any, UIBase<any>> = new Map<any, UIBase<any>>();
    private notifyMap = new Map<any, Array<UIBase<any>>>();
    private eventCache = new Map<any, any>();
    public awake() {
        UIManager.Instance = this;
        let uiroot = new Node("UIRoot");
        let uitranform = uiroot.addComponent(UITransform); uitranform.contentSize = size(screen.height, screen.height);
        let canvas = uiroot.addComponent(Canvas);
        let widget = uiroot.addComponent(Widget); widget.alignFlags = (56 | 7);
        let cameraNode = new Node("UICamera");
        let camera = cameraNode.addComponent(Camera);
        camera.far = 2000;
        camera.visibility = 1 << 23 | 1 << 25;
        camera.projection = renderer.scene.CameraProjection.ORTHO;
        camera.clearFlags = 2;
        canvas.cameraComponent = camera;
        canvas.alignCanvasWithScreen = true;
        cameraNode.parent = uiroot;

        for (let index = 1; index < UILayer.Max; index++) {
            let uilayer = new Node(`Layer${index}`);
            let uitranform = uilayer.addComponent(UITransform); uitranform.contentSize = size(screen.height, screen.height);
            uilayer.parent = uiroot;
            let panelWidget = uilayer.addComponent(Widget); panelWidget.target = uiroot; panelWidget.alignFlags = (56 | 7);
        }
        this.UIRoot = uiroot;
        game.addPersistRootNode(this.UIRoot);

        for (const kv of uiPanels) {
            let ui = new kv[0].constructor() as UIBase<any>;
            this.uiDic.set(kv[0], ui);
            let uinotifyevents = ui.events();
            for (const event of uinotifyevents) {
                let uitypes = this.notifyMap.get(event.prototype);
                if (uitypes == null) {
                    uitypes = new Array<UIBase<any>>();
                }
                uitypes.push(ui);
            }
        }
    }
    public async init<T extends UIBase<any>>(panel: new () => T) {
        let ui = this.uiDic.get(panel.prototype);
        if (!ui.inited) {
            let proto = Object.getPrototypeOf(this);
            let panelInfo = this.GetPanelInfo(proto);
            if (panelInfo.bundleUrl) {
                let prefab = await loadAysnc<Prefab>(panelInfo.bundleUrl, panelInfo.prefabUrl);
                let uiNode = instantiate(prefab);
                ui.content = uiNode.getComponent(UIReference);
                ui.content.parent = this.UIRoot.getChildByName(`Layer${panelInfo.UILayer}`);
            }
            await ui.init();
            this.publishByCache(ui);
        }
    }
    public async show<T extends UIBase<any>>(panel: new () => T) {
        let ui = this.uiDic.get(panel.prototype);
        if (!ui.inited) {
            await this.init(panel);
        }
        await ui.show();
        return ui as T;
    }
    public async hide<T extends UIBase<any>>(panel: new () => T) {
        let ui = this.uiDic.get(panel.prototype);
        await ui.hide();
    }
    public async close<T extends UIBase<any>>(panel: new () => T) {
        let ui = this.uiDic.get(panel.prototype);
        let panelInfo = UIManager.Instance.GetPanelInfo(Object.getPrototypeOf(this));
        if (panelInfo.bundleUrl) {
            let prefab = await loadAysnc<Prefab>(panelInfo.bundleUrl, panelInfo.prefabUrl);
            ui.content.destroy();
            assetManager.releaseAsset(prefab);
        }
        await ui.close();
    }
    public GetPanelInfo(target) {
        return uiPanels.get(target);
    }
    public publish(eventArg: any, isCache = true): void {
        let proto = Object.getPrototypeOf(eventArg);
        let uis = this.notifyMap.get(proto);
        for (const ui of uis) {
            if (ui.inited) {
                ui.handle(eventArg);
            }
        }
        if (isCache) {
            this.eventCache.set(proto, eventArg);
        }
    }
    public publishByCache(ui: UIBase<any>) {
        let events = ui.events();
        for (const event of events) {
            let cache = this.eventCache.get(event.prototype);
            if (cache) {
                ui.handle(cache);
            }
        }
    }
}
@et.objectSystem(UIManager)
class UIManagerAakesystem extends et.AwakeSystem<UIManager>{
    awake(self: UIManager): void {
        self.awake();
    }
}